package core.vols;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;

import core.Avion;
import core.Output;
import core.compagnie.CompAerienne;
import core.voyageur.Billet;
import core.voyageur.Client;
import exception.VolAnnuleException;
import exception.VolRetardeException;

import map.Airport;
import serveur.SimulationServeur;
import time.Heure;

public class VolInstance extends Vol {
	
	private Date date;
	private Avion avion;
	private int numero;
	private Heure tpsVolRestantEstime;
	private double distanceRestante;
	private boolean launched;
	private static Heure heureLocale;
	private SimulationServeur serveur;
	private int dureeReelle=0;
	private boolean aterrit=false;
	private ArrayList<Client> candidat;
	private int nbRetard=0;
	
	
	/**
	 * @param depart
	 * @param arrivee
	 * @param heureDepart
	 * @param duree
	 * @param date
	 * @param avion
	 * @param numero
	 * @param volCommercial
	 */
	public VolInstance(String code, Airport depart, Airport arrivee, Heure heureDepart,
			Heure duree, Date date, Avion avion, int numero, VolCommercial volCommercial, SimulationServeur serveur) throws Exception {
		super(code, depart, arrivee, heureDepart, duree, volCommercial);
		this.date = date;
		this.avion = avion;
		this.numero = numero;
		launched = false;
		tpsVolRestantEstime = duree;
		distanceRestante = distance;
		this.serveur=serveur;
		try {
			assignAvion();	
		} catch (Exception e) {
			if(nbRetard<3) {
				Output.imprimer("Vol "+numero+": "+e.getMessage());
				nbRetard++;
				throw new VolRetardeException(new Heure(0,30), this);
			} else throw new VolAnnuleException(this);
		}
		this.candidat = new ArrayList<Client>();
	}
	
	/**
	 * @param depart
	 * @param arrivee
	 * @param heureDepart
	 * @param duree
	 * @param date
	 * @param avion
	 * @param numero
	 * @param volFret
	 */
	public VolInstance(String code, Airport depart, Airport arrivee, Heure heureDepart,
			Heure duree, Date date, Avion avion, int numero, VolFret volFret, SimulationServeur serveur) throws Exception {
		super(code, depart, arrivee, heureDepart, duree, volFret);
		this.date = date;
		this.avion = avion;
		this.numero = numero;
		launched = false;
		tpsVolRestantEstime = duree;
		distanceRestante = distance;
		this.serveur=serveur;
		try {
			assignAvion();	
		} catch (Exception e) {
			if(nbRetard<3) {
				Output.imprimer("Vol "+numero+": "+e.getMessage());
				nbRetard++;
				throw new VolRetardeException(new Heure(0,30), this);
			} else throw new VolAnnuleException(this);
		}
		this.candidat = new ArrayList<Client>();
	}
	
	/**
	 * @param depart
	 * @param arrivee
	 * @param heureDepart
	 * @param duree
	 * @param date
	 * @param avion
	 * @param numero
	 * @param volCommercial
	 * @param volFret
	 */
	public VolInstance(String code, Airport depart, Airport arrivee, Heure heureDepart,
			Heure duree, Date date, Avion avion, int numero, VolCommercial volCommercial, VolFret volFret, SimulationServeur serveur) throws Exception{
		super(code, depart, arrivee, heureDepart, duree, volFret, volCommercial);
		this.date = date;
		this.avion = avion;
		this.numero = numero;
		launched = false;
		tpsVolRestantEstime = duree;
		distanceRestante = distance;
		this.serveur=serveur;
		try {
			assignAvion();	
		} catch (Exception e) {
			if(nbRetard<3) {
				Output.imprimer("Vol "+numero+": "+e.getMessage());
				nbRetard++;
				throw new VolRetardeException(new Heure(0,30), this);
			} else throw new VolAnnuleException(this);
		}
		this.candidat = new ArrayList<Client>();
	}
	
	public VolInstance(Vol v, Date d, SimulationServeur serveur, int numero) throws Exception{
		super(v);
		this.date=d;
		launched = false;
		tpsVolRestantEstime = duree;
		distanceRestante = distance;
		this.serveur=serveur;
		this.numero = numero;
		try {
			assignAvion();	
		} catch (Exception e) {
			if(nbRetard<3) {
				Output.imprimer("Vol "+numero+": "+e.getMessage());
				nbRetard++;
				throw new VolRetardeException(new Heure(0,30), this);
			} else throw new VolAnnuleException(this);
		}
		this.candidat = new ArrayList<Client>();
	}
	
	public VolInstance(Vol v, Date d, SimulationServeur serveur, int numero, Avion avion) throws Exception{
		super(v);
		this.date=d;
		launched = false;
		tpsVolRestantEstime = duree;
		distanceRestante = distance;
		this.serveur=serveur;
		this.numero = numero;
		this.avion=avion;
		this.candidat = new ArrayList<Client>();
	}
	
	
	public void assignAvion() throws Exception {
		Iterator<CompAerienne> itc = serveur.getCompagnies().values().iterator();
		Boolean avionFound = false;
		while(itc.hasNext() && !avionFound) {
			CompAerienne current = itc.next();
			ArrayList<Avion> flotte = current.getFlotte();
			for(Avion a : flotte) {
				if(a.getPlanDeVol() == null) {
					PlanDeVol pv = new PlanDeVol(a, new ArrayList<VolInstance>());
					pv.addVol(this);
					a.setPlanDeVol(pv);
					avionFound=true;
					this.avion=a;
					break;
				} else {
					PlanDeVol pv = a.getPlanDeVol();
					if(pv.isFreeAt(heureDepart, heureArrivee) && (pv.positionAt(heureDepart) == null || pv.positionAt(heureDepart).equals(depart))) {
						pv.addVol(this);
						a.setPlanDeVol(pv);
						this.avion=a;
						avionFound=true;
						break;
					}
				}
			}
		}
		if(!avionFound) throw new Exception("Aucun avion disponible");
	}

	/**
	 * @return the numero
	 */
	public int getNumero() {
		return numero;
	}

	/**
	 * @return the avion
	 */
	public Avion getAvion() {
		return avion;
	}

	/**
	 * @return the date
	 */
	public Date getDate() {
		return date;
	}
	
	public void decalerVol(Heure duree) {
		heureDepart.addDuree(duree);
	}
	
	public void launch() {

		launched=true;
		heureLocale = serveur.getHeureLocale();
		avion.doFlight(this);
		remplirVol ();
		for(Client c : candidat) {
			Output.imprimer(c+" vient de rater le vol "+this);
			Billet b = c.getBillets().get(0);
			ArrayList<VolInstance> vols = b.getVols();
			int posVol=getPosVol(vols, this);
			if(posVol != -1) {
				VolInstance volSuivant = serveur.getVolSuivant(this);
				if(volSuivant != null) b.remplacerVol(this, volSuivant);
				for(int j=posVol+1;j<vols.size();j++) {
					vols.get(j).removeCandidat(c);
					VolInstance volSuivantBoucle = serveur.getVolSuivant(vols.get(j));
					if(volSuivantBoucle != null) b.remplacerVol(vols.get(j), serveur.getVolSuivant(vols.get(j)));
				}
			}
			

			
		}
		if(avion.getPassagers().size()>0) {
			Output.imprimer("Le vol "+numero+" décolle de " + depart + " à destination de " + arrivee);
			Output.imprimer("Passagers à bord: "+avion.getPassagers().toString());
		}
	}
	
	public int getPosVol(ArrayList<VolInstance> vols, VolInstance vol) {
		for(int i=0;i<vols.size();i++) {
			if(vols.get(i).equals(vol)) return i;
		}
		return -1;
	}
	
	public boolean avance(int distanceParcourue) {
		heureLocale = serveur.getHeureLocale();
		distanceRestante = distanceRestante - distanceParcourue;
		return distanceRestante<=0;
		
	}
	


	public double getDistanceRestante() {
		return distanceRestante;
	}
	
	public void aterrissage(){
		aterrit=true;
		avion.aterrissage(this);
	}

	public boolean isAterrit() {
		return aterrit;
	}
	
	public String toString() {
		SimpleDateFormat df = new SimpleDateFormat("d/M/y");
		return super.toString()+"<"+df.format(date)+">";
	}

	public void addCandidat (Client c) {
		candidat.add(c);
	}
	
	public void removeCandidat (Client c) {
		candidat.remove(c);
	}
	
	/* Remplit l'avion et retourne la liste des passagers n'ayant pas put y entrer */
	public ArrayList<Client> remplirVol () {
		for (int i=0 ; i<candidat.size() && !avion.estPlein() ; i++) {
			if (candidat.get(i).contientCarte(avion.getCompagnie()) || candidat.get(i).contientCarte(avion.getCompagnie().getMetaCompAssocie())) {
				avion.addPassager(candidat.get(i));
				candidat.remove(candidat.get(i));
			}
		}
		for (int i=0 ; i<candidat.size() && !avion.estPlein() ; i++) {
			avion.addPassager(candidat.get(i));
			candidat.remove(candidat.get(i));
		}
		
		return candidat;
	}
	
	public void setPlein() {
		avion.setPlein();
	}
	
	

}
